
package cn.tang.tframe.springredis;

import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import cn.tang.tframe.common.base.MainConfigLoader;
import cn.tang.tframe.common.utils.io.PropertiesUtil;
import cn.tang.tframe.dynconfig.DynConfigRegister;
import cn.tang.tframe.dynconfig.IDynConfigProcessor;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Created by tanghc on 17/1/13.
 *
 * 获得项目相关的redis客户端。
 * 建议在使用cache的地方调用 QtProjectRedisClient.getSpringRedisCache()获取
 * 这样可以保持最新的客户端(若redis配置信息修改了,会重新生成客户端)
 * 否则始终保持一个客户端必须修改redis必须重启
 *
 */
public class QtProjectRedisClient {

	private final static Logger logger =
		LoggerFactory.getLogger(QtProjectRedisClient.class);

	private QtProjectRedisClient() {
	}

	private static class QtProjectRedisClientHolder {

		public static ISpringRedisCache instance = null;
		public static JedisConnectionFactory jedisConnectionFactory = null;
		private static final String redis_config_group = "common";
		private static final String redis_config_dataId = "redisConfig";
		private static final String productCode =
			MainConfigLoader.getInstance().getProductCode();
		private static final String appCode =
			MainConfigLoader.getInstance().getAppCode();
		static {
			DynConfigRegister.getInstance().buildOnce(productCode, appCode, redis_config_group, redis_config_dataId, new IDynConfigProcessor() {
				@Override
				public void procesConfig(String config) {
					JedisConnectionFactory jedisConnectionFactory = buildNewJedisConnectionFactory(PropertiesUtil.loadFromString(config));
					if(null != jedisConnectionFactory){
						ISpringRedisCache springRedisCache =
								buildNewRedisRepository(jedisConnectionFactory);
//								创建客户端成功情况下才修改单例
						if (null != springRedisCache) {
							instance = springRedisCache;
							logger.warn(
									"modify redis info create new redisclient:productCode={},appCode={}",
									productCode, appCode);
						}else{
							logger.warn(
									"didnot modify redis info create new redisclient no springrediscache:productCode={},appCode={}",
									productCode, appCode);
						}
					}else{
						logger.warn(
								"didnot modify redis info create new redisclient no jedisconnection factory :productCode={},appCode={}",
								productCode, appCode);
					}

				}
			});
		}

		private static String getProjectPrefix(
			String productCode, String appCode) {

			return new StringBuilder().append(productCode).append(":").append(
				appCode).toString();
		}

		/**
		 * always return new
		 *
		 * @param properties
		 * @return
		 */
		public static JedisConnectionFactory buildNewJedisConnectionFactory(
			Properties properties) {

			JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
			// jedisPoolConfig.setMaxActive(Integer.parseInt(properties.getProperty("redis.pool.maxActive",
			// String.valueOf(jedisPoolConfig.getMaxActive()))));
			jedisPoolConfig.setMaxIdle(
				Integer.parseInt(
					properties.getProperty(
						"redis.pool.maxIdle",
						String.valueOf(jedisPoolConfig.getMaxIdle()))));
			jedisPoolConfig.setMaxTotal(
				Integer.parseInt(
					properties.getProperty(
						"redis.pool.maxTotal",
						String.valueOf(jedisPoolConfig.getMaxTotal()))));
			jedisPoolConfig.setMaxWaitMillis(
				Integer.parseInt(
					properties.getProperty(
						"redis.pool.maxWaitMillis",
						String.valueOf(jedisPoolConfig.getMaxWaitMillis()))));
			// jedisPoolConfig.setMaxWait(Integer.parseInt(properties.getProperty("redis.pool.maxWait",
			// String.valueOf(jedisPoolConfig.getMaxWait()))));
			jedisPoolConfig.setMinEvictableIdleTimeMillis(
				Integer.parseInt(
					properties.getProperty(
						"redis.pool.minEvictableIdleTimeMillis", String.valueOf(
							jedisPoolConfig.getMinEvictableIdleTimeMillis()))));
			jedisPoolConfig.setMinIdle(
				Integer.parseInt(
					properties.getProperty(
						"redis.pool.minIdle",
						String.valueOf(jedisPoolConfig.getMinIdle()))));
			jedisPoolConfig.setNumTestsPerEvictionRun(
				Integer.parseInt(
					properties.getProperty(
						"redis.pool.numTestsPerEvictionRun", String.valueOf(
							jedisPoolConfig.getNumTestsPerEvictionRun()))));
			jedisPoolConfig.setSoftMinEvictableIdleTimeMillis(
				Integer.parseInt(
					properties.getProperty(
						"redis.pool.minEvictableIdleTimeMillis", String.valueOf(
							jedisPoolConfig.getMinEvictableIdleTimeMillis()))));
			jedisPoolConfig.setTestOnBorrow(
				Boolean.parseBoolean(
					properties.getProperty(
						"redis.pool.testOnBorrow",
						String.valueOf(jedisPoolConfig.getTestOnBorrow()))));
			jedisPoolConfig.setTestOnReturn(
				Boolean.parseBoolean(
					properties.getProperty(
						"redis.pool.testOnReturn",
						String.valueOf(jedisPoolConfig.getTestOnReturn()))));
			jedisPoolConfig.setTestWhileIdle(
				Boolean.parseBoolean(
					properties.getProperty(
						"redis.pool.testWhileIdle",
						String.valueOf(jedisPoolConfig.getTestWhileIdle()))));
			jedisPoolConfig.setTimeBetweenEvictionRunsMillis(
				Integer.parseInt(
					properties.getProperty(
						"redis.pool.timeBetweenEvictionRunsMillis",
						String.valueOf(
							jedisPoolConfig.getTimeBetweenEvictionRunsMillis()))));
			// jedisPoolConfig.set
			// jedisPoolConfig.setWhenExhaustedAction(Byte.parseByte(properties.getProperty("redis.pool.whenExhaustedAction",
			// String.valueOf(jedisPoolConfig.getWhenExhaustedAction()))));
			JedisConnectionFactory jedisConnectionFactory =
				new JedisConnectionFactory(jedisPoolConfig);
			jedisConnectionFactory.setDatabase(
				Integer.parseInt(
					properties.getProperty(
						"redis.database",
						String.valueOf(jedisConnectionFactory.getDatabase()))));
			jedisConnectionFactory.setHostName(
				properties.getProperty(
					"redis.ip", jedisConnectionFactory.getHostName()));
			jedisConnectionFactory.setPassword(
				properties.getProperty(
					"redis.password", jedisConnectionFactory.getPassword()));
			jedisConnectionFactory.setPort(
				Integer.parseInt(
					properties.getProperty(
						"redis.port",
						String.valueOf(jedisConnectionFactory.getPort()))));

			// important
			jedisConnectionFactory.afterPropertiesSet();

			return jedisConnectionFactory;
		}
		/**
		 * always return new
		 *
		 * @param properties
		 * @return
		 */
		public static ISpringRedisCache buildNewRedisRepository(
			Properties properties) {

			RedisTemplate redisTemplate = new RedisTemplate();
			redisTemplate.setConnectionFactory(buildNewJedisConnectionFactory(properties));

			// StringRedisSerializer
			StringRedisSerializer serializer = new StringRedisSerializer();
			redisTemplate.setKeySerializer(serializer);
			redisTemplate.setHashKeySerializer(serializer);
			// redisTemplate.setHashValueSerializer(serializer);

			// important
			redisTemplate.afterPropertiesSet();

			QtProjectRedisRepository redisRepository =
				new QtProjectRedisRepository();
			redisRepository.setRedisTemplate(redisTemplate);
			redisRepository.setPrefix(getProjectPrefix(productCode, appCode));

			return redisRepository;
		}
		/**
		 * always return new
		 *
		 * @param properties
		 * @return
		 */
		public static ISpringRedisCache buildNewRedisRepository(JedisConnectionFactory jedisConnectionFactory) {

			RedisTemplate redisTemplate = new RedisTemplate();
			redisTemplate.setConnectionFactory(jedisConnectionFactory);

			// StringRedisSerializer
			StringRedisSerializer serializer = new StringRedisSerializer();
			redisTemplate.setKeySerializer(serializer);
			redisTemplate.setHashKeySerializer(serializer);
			// redisTemplate.setHashValueSerializer(serializer);

			// important
			redisTemplate.afterPropertiesSet();

			QtProjectRedisRepository redisRepository =
				new QtProjectRedisRepository();
			redisRepository.setRedisTemplate(redisTemplate);
			redisRepository.setPrefix(getProjectPrefix(productCode, appCode));

			return redisRepository;
		}
	}

	public static <V> ISpringRedisCache<V> getSpringRedisCache() {

		return QtProjectRedisClientHolder.instance;
	}
	public static JedisConnectionFactory getJedisConnectionFactory() {
		return QtProjectRedisClientHolder.jedisConnectionFactory;
	}

	public static JedisConnection getJedisConnection() {
		return QtProjectRedisClientHolder.jedisConnectionFactory.getConnection();
	}

	public static Jedis getJedis() {
		return QtProjectRedisClientHolder.jedisConnectionFactory.getConnection().getNativeConnection();
	}
}
